/*
 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <apb_map.h>
#include <ath_chip.h>
#include <955x.h>

/*
 * Helper macros.
 * These Clobber t7, t8 and t9
 */
#define reg_write(_reg, _val)			\
	li	t7,	KSEG1ADDR(_reg);	\
	li	t8,	_val;			\
	sw	t8,	0(t7);

#define reg_rmw_set(_reg, _mask, _val)		\
	li	t7,	KSEG1ADDR(_reg);	\
	lw	t8,	0(t7);			\
	li	t9,	~(_mask);		\
	and	t8,	t8,	t9;		\
	li	t9,	_val;			\
	or	t8,	t8,	t9;		\
	sw	t8,	0(t7)

#define cpu_pll_set(_mask, _val)		\
	reg_rmw_set(CPU_PLL_CONFIG_ADDRESS, _mask, _val)

#define ddr_pll_set(_mask, _val)		\
	reg_rmw_set(DDR_PLL_CONFIG_ADDRESS, _mask, _val)

#define cpu_ddr_control_set(_mask, _val)	\
	reg_rmw_set(CPU_DDR_CLOCK_CONTROL_ADDRESS, _mask, _val)


/******************************************************************************
 * first level initialization:
 *
 * 0) If clock cntrl reset switch is already set, we're recovering from
 *    "divider reset"; goto 3.
 * 1) Setup divide ratios.
 * 2) Reset.
 * 3) Setup pll's, wait for lock.
 *
 *****************************************************************************/

.globl lowlevel_init
	.type	lowlevel_init, @function
	.text
	.align 4

lowlevel_init:

#ifdef CONFIG_MACH_QCA955x

#define jrcd		1
#define iagn		!jrcd

#if iagn && jrcd
#error Both iagn and jrcd set
#endif

#if jrcd
	/*
	 * JR Cache Prediction Disable. Disables JR target address prediction.
	 * Bit [0], CP0 Register 16, Select 6
	 *	0 - JR cache target address prediction is enabled.
	 *	1 - JR cache target address prediction is not enabled.
	 */
	mfc0	t0,	$16,	6
	li	t1,	(1 << 0)
	or	t0,	t0,	t1
	mtc0	t0,	$16,	6
#endif

#if iagn
	/*
	 * JR Cache Prediction Disable. Disables JR target address prediction.
	 * Bit [25], CP0 Register 16, Select 7
	 * Selective control of out-of-order behavior: issue ALU-side or
	 * load/store-side instructions (respectively) in program order.
	 */
	mfc0	t0,	$16,	7
	li	t1,	(1 << 25)
	or	t0,	t0,	t1
	mtc0	t0,	$16,	7
#endif
#endif /* CONFIG_MACH_QCA955x */

#if !defined(CONFIG_ATH_EMULATION)

	reg_write(BB_DPLL2_ADDRESS, BB_DPLL2_KI_SET(4) | \
				BB_DPLL2_KD_SET(0x60) | \
				BB_DPLL2_PLL_PWD_SET(1) | \
				BB_DPLL2_DELTA_SET(0x1e));
	reg_write(PCIe_DPLL2_ADDRESS, PCIe_DPLL2_KI_SET(4) | \
				PCIe_DPLL2_KD_SET(0x60) | \
				PCIe_DPLL2_PLL_PWD_SET(1) | \
				PCIe_DPLL2_DELTA_SET(0x1e));
	reg_write(DDR_DPLL2_ADDRESS, DDR_DPLL2_KI_SET(4) | \
				DDR_DPLL2_KD_SET(0x60) | \
				DDR_DPLL2_PLL_PWD_SET(1) | \
				DDR_DPLL2_DELTA_SET(0x1e));
	reg_write(CPU_DPLL2_ADDRESS, CPU_DPLL2_KI_SET(4) | \
				CPU_DPLL2_KD_SET(0x60) | \
				CPU_DPLL2_PLL_PWD_SET(1) | \
				CPU_DPLL2_DELTA_SET(0x1e));

	li	t5,	CPU_PLL_CONFIG_NINT_VAL
	li	t6,	DDR_PLL_CONFIG_NINT_VAL
	li	t4,	CPU_PLL_DITHER_VAL
	li	t3,	DDR_PLL_DITHER_VAL

pll_bypass_set:
	cpu_ddr_control_set (CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_SET(1));
	cpu_ddr_control_set (CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_SET(1));
	cpu_ddr_control_set (CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_SET(1));

init_cpu_pll:
	li	t7,	KSEG1ADDR(CPU_PLL_CONFIG_ADDRESS);
	li	t8,	(CPU_PLL_CONFIG_PLLPWD_SET(1) | \
			 CPU_PLL_CONFIG_REF_DIV_VAL | \
			 CPU_PLL_CONFIG_RANGE_VAL | \
			 CPU_PLL_CONFIG_OUT_DIV_VAL1);
	or	t8,	t8,	t5
	sw	t8,	0(t7);

init_ddr_pll:
	li	t7,	KSEG1ADDR(DDR_PLL_CONFIG_ADDRESS);
	li	t8,	(DDR_PLL_CONFIG_PLLPWD_SET(1) | \
			 DDR_PLL_CONFIG_REF_DIV_VAL | \
			 DDR_PLL_CONFIG_RANGE_VAL | \
			 DDR_PLL_CONFIG_OUT_DIV_VAL1);
	or	t8,	t8,	t6
	sw	t8,	0(t7);

init_ahb_pll:
	reg_write(CPU_DDR_CLOCK_CONTROL_ADDRESS,
			CPU_DDR_CLOCK_CONTROL_AHB_DIV_VAL |
			AHB_CLK_FROM_DDR |
			CPU_AND_DDR_CLK_FROM_DDR |
			CPU_AND_DDR_CLK_FROM_CPU |
			CPU_DDR_CLOCK_CONTROL_DDR_POST_DIV |
			CPU_DDR_CLOCK_CONTROL_CPU_POST_DIV |
			CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_SET(1) |
			CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_SET(1) |
			CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_SET(1));

pll_pwd_unset:
	cpu_pll_set(CPU_PLL_CONFIG_PLLPWD_MASK, CPU_PLL_CONFIG_PLLPWD_SET(0));
	ddr_pll_set(DDR_PLL_CONFIG_PLLPWD_MASK, DDR_PLL_CONFIG_PLLPWD_SET(0));

outdiv_unset:
	cpu_pll_set(CPU_PLL_CONFIG_OUTDIV_MASK, CPU_PLL_CONFIG_OUT_DIV_VAL2);
	ddr_pll_set(DDR_PLL_CONFIG_OUTDIV_MASK, DDR_PLL_CONFIG_OUT_DIV_VAL2);

pll_bypass_unset:
	cpu_ddr_control_set(CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_SET(0));
	cpu_ddr_control_set(CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_SET(0));
	cpu_ddr_control_set(CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_MASK, CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_SET(0));

ddr_pll_dither_unset:
	li	t7,	KSEG1ADDR(DDR_PLL_DITHER_ADDRESS);
	sw	t3,	0(t7);

cpu_pll_dither_unset:
	li	t7,	KSEG1ADDR(CPU_PLL_DITHER_ADDRESS);
	sw	t4,	0(t7);

#endif /* !defined(CONFIG_ATH_EMULATION) */
	jr ra
	nop
